﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NRadioTray.App.Domain;
using NRadioTray.App.Options;
using NRadioTray.App.Player;
using log4net;

namespace NRadioTray.App
{
    /// <summary>
    /// MainView object instance.
    /// </summary>
    public partial class MainView : Form, IPlayerObserver
    {
        /// <summary>
        /// 
        /// </summary>
        IPlayer player;

        /// <summary>
        /// 
        /// </summary>
        Radio currentPlay;

        /// <summary>
        /// 
        /// </summary>
        private readonly ILog log;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="status">The status.</param>
        /// <param name="message">The message.</param>
        delegate void SetStatusCallback(PlayerState status, string message);

        [ImportMany(typeof(Interface.IPlugin))]
        private  Interface.IPlugin[] Plugins { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainView"/> class.
        /// </summary>
        /// <param name="log">The log.</param>
        public MainView(ILog log)
        {
            InitializeComponent();
            this.log = log;
            this.SetView();            
        }

        /// <summary>
        /// Gets the player.
        /// </summary>
        public IPlayer Player
        {
            get
            {
                if (this.player == null)
                {
                    this.player = new SimplePlayer(this, this.log);
                }

                return this.player;
            }
        }

        /// <summary>
        /// Sets the status.
        /// </summary>
        /// <param name="status">The status.</param>
        /// <param name="message">The message.</param>
        public void SetStatus(PlayerState status, string message)
        {
            this.Invoke(new SetStatusCallback(this.SetStatusView), status, message);
        }

        /// <summary>
        /// Sets the status1.
        /// </summary>
        /// <param name="status">The status.</param>
        /// <param name="message">The message.</param>
        private void SetStatusView(PlayerState status, string message)
        {
            bool addDots = false;

            if (this.currentPlay != null)
            {

                while (TextRenderer.MeasureText(string.Format("{0}...", this.currentPlay.Name), this.lblRadioName.Font).Width > this.lblRadioName.Width)
                {
                    this.currentPlay.Name = this.currentPlay.Name.Substring(0, this.currentPlay.Name.Length - 1);
                    addDots = true;
                }

                if (addDots)
                {
                    this.currentPlay.Name = string.Format("{0}...", this.currentPlay.Name);
                }

                switch (status)
                {
                    case PlayerState.Playing:
                        this.lblRadioName.Text = this.currentPlay.Name;
                        this.lblStatus.Text = "Playing";
                        this.menuItemStatus.Enabled = true;
                        this.menuItemPause.Enabled = true;
                        this.radioTray.Icon = Properties.Resources.nradiotray_on_ico;
                        break;
                    case PlayerState.Buffering:
                        this.lblRadioName.Text = this.currentPlay.Name;
                        this.lblStatus.Text = "Buffering";
                        this.menuItemStatus.Enabled = false;
                        this.menuItemPause.Enabled = false;
                        this.radioTray.Icon = Properties.Resources.nradiotray_connecting_ico;
                        break;
                    case PlayerState.Paused:
                        this.lblRadioName.Text = this.currentPlay.Name;
                        this.lblStatus.Text = "Paused";
                        this.menuItemStatus.Enabled = true;
                        this.menuItemPause.Enabled = true;
                        this.radioTray.Icon = Properties.Resources.nradiotray_on_ico;
                        break;
                    case PlayerState.Stopped:
                        this.lblRadioName.Text = string.Empty;
                        this.lblStatus.Text = "Stopped";
                        this.menuItemStatus.Enabled = false;
                        this.menuItemPause.Enabled = false;
                        this.radioTray.Icon = Properties.Resources.nradiotray_off_ico;
                        break;
                    case PlayerState.Error:
                        this.lblRadioName.Text = message;
                        this.lblStatus.Text = "Error";
                        this.menuItemStatus.Enabled = false;
                        this.menuItemPause.Enabled = false;
                        this.radioTray.Icon = Properties.Resources.nradiotray_off_ico;
                        break;
                    case PlayerState.EndBuffering:
                        this.lblRadioName.Text = message;
                        this.lblStatus.Text = "End buffering";
                        this.menuItemStatus.Enabled = true;
                        this.menuItemPause.Enabled = true;
                        this.radioTray.Icon = Properties.Resources.nradiotray_on_ico;
                        break;
                }

                this.lblMessage.Text = message;

                this.StartShowPopupTimer();
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Form.Shown"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);
            this.StartShowPopupTimer();
            this.radioTray.ContextMenuStrip.Opening += this.ContextMenuStripOpening;
        }

        /// <summary>
        /// Handles the Opening event of the ContextMenuStrip control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void ContextMenuStripOpening(object sender, CancelEventArgs e)
        {
            this.BuildRadiosList();
            this.BuildPluginsList();
        }

        private void Compose()
        {
            AggregateCatalog aggregateCatalog = new AggregateCatalog();
            aggregateCatalog.Catalogs.Add(new DirectoryCatalog(".\\Plugins"));


            CompositionContainer container = new CompositionContainer(aggregateCatalog);
            container.ComposeParts(this);

        }

        private void BuildPluginsList()
        {
            using (new WaitCursor())
            {
                this.Compose();

                var playlist = RadioMenuPluginsBuilder.GeneratePlugins(this.Plugins.ToList());

                if (playlist == null)
                {
                    return;
                }

                this.menuItemPlugins.DropDownItemClicked -= this.PluginsItemClick;
                this.menuItemPlugins.DropDownItems.Clear();
                this.menuItemPlugins.DropDownItems.AddRange(playlist);
                this.menuItemPlugins.DropDownItemClicked += this.PluginsItemClick;
            }
        }

        /// <summary>
        /// Sets the view.
        /// </summary>
        private void SetView()
        {
            this.ShowInTaskbar = false;            
            var primarySize = Screen.PrimaryScreen.WorkingArea.Size;
            this.Location = new Point(primarySize.Width - this.Width - 3, primarySize.Height - this.Height-3);
        }

        /// <summary>
        /// Menus the item exit click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MenuItemExitClick(object sender, EventArgs e)
        {
            this.Player.Dispose();
            Application.Exit();
        }

        /// <summary>
        /// Starts the show popup timer.
        /// </summary>
        private void StartShowPopupTimer()
        {
            if (this.timer.Enabled)
            {
                this.timer.Stop();
            }

            this.timer.Start();
            this.ShowPopup();
        }

        /// <summary>
        /// Timers the tick.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TimerTick(object sender, EventArgs e)
        {
            if (sender is Timer)
            {
                (sender as Timer).Stop();
                this.HidePopup();
            }
        }

        /// <summary>
        /// Builds the radios list.
        /// </summary>
        private void BuildRadiosList()
        {
            using (new WaitCursor())
            {
                var playlist = RadioMenuPlaylistBuilder.GeneratePlaylist();

                if (playlist == null)
                {
                    return;
                }

                this.menuItemPlaylist.DropDownItemClicked -= this.PlaylistItemClick;
                this.menuItemPlaylist.DropDownItems.Clear();
                this.menuItemPlaylist.DropDownItems.AddRange(playlist);
                this.menuItemPlaylist.DropDownItemClicked += this.PlaylistItemClick;
            }
        }


        private void PluginsItemClick(object sender, ToolStripItemClickedEventArgs e)
        {
            var pluginGuid = e.ClickedItem.Tag is Guid ? (Guid)e.ClickedItem.Tag : Guid.Empty;

            foreach (var plugin in this.Plugins)
            {
                if(plugin.Handler.Equals(pluginGuid))
                {
                    plugin.Run();
                }
            }
        }

        /// <summary>
        /// Playlists the item click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.ToolStripItemClickedEventArgs"/> instance containing the event data.</param>
        private void PlaylistItemClick(object sender, ToolStripItemClickedEventArgs e)
        {
            var radio = e.ClickedItem.Tag as Radio;

            if (radio == null)
            {
                return;
            }

            this.currentPlay = radio;

            this.DropPlayer();
            this.Player.Play(this.currentPlay.Url);
        }

        /// <summary>
        /// Drops the player.
        /// </summary>
        private void DropPlayer()
        {
            if (this.player != null)
            {
                this.player.Dispose();
                this.player = null;
            }
        }

        /// <summary>
        /// Radioes the tray click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RadioTrayClick(object sender, EventArgs e)
        {
            var eventArgs = e as MouseEventArgs;
            
            if (eventArgs == null)
            {
                return;
            }


            if (eventArgs.Button == MouseButtons.Left)
            {
                this.StartShowPopupTimer();
            }
        }

        /// <summary>
        /// Shows the popup.
        /// </summary>
        private void ShowPopup()
        {
            this.Visible = true;
            this.Opacity = 1;
            this.Focus();            
        }

        /// <summary>
        /// Hides the popup.
        /// </summary>
        private void HidePopup()
        {
            this.Visible = false;
            this.Hide();
        }

        /// <summary>
        /// Menus the item status click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MenuItemStatusClick(object sender, EventArgs e)
        {
            this.Player.Stop();
            this.DropPlayer();
        }

        /// <summary>
        /// Menus the item options click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void MenuItemOptionsClick(object sender, EventArgs e)
        {
            this.ShowOptions();
        }

        /// <summary>
        /// Radioes the tray double click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RadioTrayDoubleClick(object sender, EventArgs e)
        {
            this.ShowOptions();
        }

        /// <summary>
        /// Shows the options.
        /// </summary>
        private void ShowOptions()
        {
            using (var options = new Options.OptionsPresenter(new Options.OptionsView(), this.log))
            {
                options.Show();
            }
        }

        private void MenuItemPauseClick(object sender, EventArgs e)
        {
            this.menuItemPause.Checked = !this.menuItemPause.Checked;

            if (this.menuItemPause.Checked)
            {
                this.Player.Pause();
            }
            else
            {
                this.Player.Play(this.currentPlay.Url);
            }
        }

        private void radioTray_MouseMove(object sender, MouseEventArgs e)
        {
            

        }
    }
}
